import { encryptTotpSecret, decryptTotpSecret } from '../utils/crypto.js';
import { generateTotpCode, verifyTotpCode, validateTotpConfig, parseTotpUri } from '../utils/totp.js';
import { NotFoundError, ValidationError, AuthorizationError } from '../utils/errors.js';
import { logAudit, logger } from '../utils/logger.js';

export class TokenService {
  constructor(prisma) {
    this.prisma = prisma;
  }

  /**
   * 创建令牌
   */
  async createToken(userId, data, userPassword) {
    const {
      serviceName,
      accountName,
      secret,
      algorithm = 'SHA1',
      digits = 6,
      period = 30,
      tokenType = 'TOTP',
      category,
      tags,
      iconUrl,
      notes,
    } = data;
    
    // 验证TOTP配置
    const validation = validateTotpConfig({ algorithm, digits, period });
    if (!validation.valid) {
      throw new ValidationError('INVALID_INPUT', validation.errors.join(', '));
    }
    
    // 获取用户的masterKeySalt
    const user = await this.prisma.user.findUnique({
      where: { id: userId },
      select: { masterKeySalt: true },
    });
    
    if (!user) {
      throw new NotFoundError('USER_NOT_FOUND', '用户不存在');
    }
    
    // 加密TOTP密钥
    const encryptedSecret = encryptTotpSecret(secret, userPassword, user.masterKeySalt);
    
    // 创建令牌
    const token = await this.prisma.token.create({
      data: {
        userId,
        serviceName,
        accountName,
        encryptedSecret,
        algorithm,
        digits,
        period,
        tokenType,
        category,
        tags: tags ? JSON.stringify(tags) : null,
        iconUrl,
        notes,
      },
    });
    
    // 记录审计日志
    await logAudit(this.prisma, {
      userId,
      action: 'TOKEN_CREATED',
      resourceType: 'token',
      resourceId: token.id,
      status: 'success',
      details: { serviceName },
    });
    
    logger.info('Token created', { userId, tokenId: token.id, serviceName });
    
    return token;
  }

  /**
   * 导入令牌（从URI）
   */
  async importFromUri(userId, uri, userPassword) {
    try {
      const parsed = parseTotpUri(uri);
      
      return this.createToken(userId, {
        serviceName: parsed.issuer || parsed.label || 'Unknown',
        accountName: parsed.label,
        secret: parsed.secret,
        algorithm: parsed.algorithm,
        digits: parsed.digits,
        period: parsed.period,
      }, userPassword);
    } catch (error) {
      throw new ValidationError('INVALID_INPUT', 'Invalid TOTP URI: ' + error.message);
    }
  }

  /**
   * 获取用户所有令牌
   */
  async getUserTokens(userId, options = {}) {
    const {
      category,
      isFavorite,
      search,
      limit = 100,
      offset = 0,
      orderBy = 'createdAt',
      orderDir = 'desc',
    } = options;
    
    const where = { userId };
    
    if (category) {
      where.category = category;
    }
    
    if (isFavorite !== undefined) {
      where.isFavorite = isFavorite;
    }
    
    if (search) {
      where.OR = [
        { serviceName: { contains: search } },
        { accountName: { contains: search } },
        { notes: { contains: search } },
      ];
    }
    
    const [tokens, total] = await Promise.all([
      this.prisma.token.findMany({
        where,
        take: limit,
        skip: offset,
        orderBy: { [orderBy]: orderDir },
      }),
      this.prisma.token.count({ where }),
    ]);
    
    // 解析tags
    const tokensWithParsedTags = tokens.map(token => ({
      ...token,
      tags: token.tags ? JSON.parse(token.tags) : [],
      // 不返回加密的secret
      encryptedSecret: undefined,
    }));
    
    return {
      tokens: tokensWithParsedTags,
      total,
      limit,
      offset,
    };
  }

  /**
   * 获取单个令牌
   */
  async getToken(tokenId, userId, checkOwnership = true) {
    const token = await this.prisma.token.findUnique({
      where: { id: tokenId },
    });
    
    if (!token) {
      throw new NotFoundError('TOKEN_NOT_FOUND', '令牌不存在');
    }
    
    if (checkOwnership && token.userId !== userId) {
      throw new AuthorizationError('PERMISSION_DENIED', '无权访问此令牌');
    }
    
    return {
      ...token,
      tags: token.tags ? JSON.parse(token.tags) : [],
      encryptedSecret: undefined,
    };
  }

  /**
   * 更新令牌
   */
  async updateToken(tokenId, userId, data) {
    const token = await this.getToken(tokenId, userId);
    
    const { tags, ...rest } = data;
    const updateData = {
      ...rest,
      updatedAt: new Date(),
    };
    
    if (tags) {
      updateData.tags = JSON.stringify(tags);
    }
    
    const updated = await this.prisma.token.update({
      where: { id: tokenId },
      data: updateData,
    });
    
    // 记录审计日志
    await logAudit(this.prisma, {
      userId,
      action: 'TOKEN_UPDATED',
      resourceType: 'token',
      resourceId: tokenId,
      status: 'success',
    });
    
    logger.info('Token updated', { userId, tokenId });
    
    return {
      ...updated,
      tags: updated.tags ? JSON.parse(updated.tags) : [],
      encryptedSecret: undefined,
    };
  }

  /**
   * 删除令牌
   */
  async deleteToken(tokenId, userId) {
    const token = await this.getToken(tokenId, userId);
    
    await this.prisma.token.delete({
      where: { id: tokenId },
    });
    
    // 记录审计日志
    await logAudit(this.prisma, {
      userId,
      action: 'TOKEN_DELETED',
      resourceType: 'token',
      resourceId: tokenId,
      status: 'success',
      details: { serviceName: token.serviceName },
    });
    
    logger.info('Token deleted', { userId, tokenId });
  }

  /**
   * 生成TOTP验证码
   */
  async generateCode(tokenId, userId, userPassword) {
    const token = await this.prisma.token.findUnique({
      where: { id: tokenId },
      include: { user: { select: { masterKeySalt: true } } },
    });
    
    if (!token) {
      throw new NotFoundError('TOKEN_NOT_FOUND', '令牌不存在');
    }
    
    if (token.userId !== userId) {
      throw new AuthorizationError('PERMISSION_DENIED', '无权访问此令牌');
    }
    
    try {
      // 解密TOTP密钥
      const secret = decryptTotpSecret(
        token.encryptedSecret,
        userPassword,
        token.user.masterKeySalt
      );
      
      // 生成验证码
      const code = generateTotpCode(secret, {
        algorithm: token.algorithm,
        digits: token.digits,
        period: token.period,
      });
      
      // 更新使用统计
      await this.prisma.token.update({
        where: { id: tokenId },
        data: {
          usageCount: { increment: 1 },
          lastUsed: new Date(),
        },
      });
      
      return {
        code,
        period: token.period,
        remainingSeconds: token.period - (Math.floor(Date.now() / 1000) % token.period),
      };
    } catch (error) {
      logger.error('Failed to generate code', { error: error.message, tokenId });
      throw new ValidationError('INVALID_INPUT', '无法生成验证码，请检查密码');
    }
  }

  /**
   * 验证TOTP验证码
   */
  async verifyCode(tokenId, userId, code, userPassword) {
    const token = await this.prisma.token.findUnique({
      where: { id: tokenId },
      include: { user: { select: { masterKeySalt: true } } },
    });
    
    if (!token) {
      throw new NotFoundError('TOKEN_NOT_FOUND', '令牌不存在');
    }
    
    if (token.userId !== userId) {
      throw new AuthorizationError('PERMISSION_DENIED', '无权访问此令牌');
    }
    
    try {
      // 解密TOTP密钥
      const secret = decryptTotpSecret(
        token.encryptedSecret,
        userPassword,
        token.user.masterKeySalt
      );
      
      // 验证验证码
      const isValid = verifyTotpCode(code, secret, {
        algorithm: token.algorithm,
        digits: token.digits,
        period: token.period,
      });
      
      if (isValid) {
        // 更新验证时间
        await this.prisma.token.update({
          where: { id: tokenId },
          data: {
            lastVerified: new Date(),
            usageCount: { increment: 1 },
            lastUsed: new Date(),
          },
        });
      }
      
      return isValid;
    } catch (error) {
      logger.error('Failed to verify code', { error: error.message, tokenId });
      throw new ValidationError('INVALID_INPUT', '验证失败');
    }
  }

  /**
   * 切换收藏状态
   */
  async toggleFavorite(tokenId, userId) {
    const token = await this.getToken(tokenId, userId);
    
    const updated = await this.prisma.token.update({
      where: { id: tokenId },
      data: { isFavorite: !token.isFavorite },
    });
    
    return updated.isFavorite;
  }

  /**
   * 批量删除
   */
  async batchDelete(tokenIds, userId) {
    // 验证所有令牌属于该用户
    const tokens = await this.prisma.token.findMany({
      where: {
        id: { in: tokenIds },
        userId,
      },
    });
    
    if (tokens.length !== tokenIds.length) {
      throw new AuthorizationError('PERMISSION_DENIED', '部分令牌不属于当前用户');
    }
    
    const result = await this.prisma.token.deleteMany({
      where: {
        id: { in: tokenIds },
        userId,
      },
    });
    
    // 记录审计日志
    await logAudit(this.prisma, {
      userId,
      action: 'TOKENS_BATCH_DELETED',
      status: 'success',
      details: { count: result.count },
    });
    
    logger.info('Tokens batch deleted', { userId, count: result.count });
    
    return result.count;
  }

  /**
   * 导出令牌（不包含密钥）
   */
  async exportTokens(userId) {
    const tokens = await this.prisma.token.findMany({
      where: { userId },
      select: {
        serviceName: true,
        accountName: true,
        algorithm: true,
        digits: true,
        period: true,
        tokenType: true,
        category: true,
        tags: true,
        notes: true,
        isFavorite: true,
        createdAt: true,
      },
    });
    
    return tokens.map(token => ({
      ...token,
      tags: token.tags ? JSON.parse(token.tags) : [],
    }));
  }
}


